﻿using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media.Media3D;
using LFSRecord2.Events;
using LFSRecord2.Model;
using LFSRecord2.Model.UserActions;
using LFSRecord2.Structures;
using LFSRecord2.View.Converters;
using LFSRecord2.View.Dialogs;

namespace LFSRecord2.View.TimelineStructures
{
    /// <summary>
    /// Interaction logic for KeyImageControl.xaml
    /// </summary>
    public partial class KeyImageControl : Image
    {
        public static readonly DependencyProperty KeyImageTypeProperty = DependencyProperty.Register(
            "KeyImageType",
            typeof(KeyImageTypes),
            typeof(KeyImageControl),
            new FrameworkPropertyMetadata(new PropertyChangedCallback(OnKeyImageTypeChanged))
        );
        public KeyImageTypes KeyImageType
        {
            get { return (KeyImageTypes)GetValue(KeyImageTypeProperty); }
            set { SetValue(KeyImageTypeProperty, value); }
        }

        public KeyImageControl()
        {
            InitializeComponent();
        }

        private void KeyImage_Loaded(object sender, RoutedEventArgs e)
        {
            CustomKeyFrame kf = (CustomKeyFrame)DataContext;

            // Bind KeyImageType
            Binding myBinding = new Binding("KeyImageType");
            myBinding.Mode = BindingMode.TwoWay;
            myBinding.Source = kf;
            SetBinding(KeyImageControl.KeyImageTypeProperty, myBinding);

            KeyImageType = kf.KeyImageType;
            SetSource();
        }

        private static void OnKeyImageTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((KeyImageControl)d).SetSource();
        }

        private void SetSource()
        {
            switch (KeyImageType)
            {
                case KeyImageTypes.KFNormal:
                    this.Source = BitmapCache.KFNormal;
                    break;
                case KeyImageTypes.KFNormalS:
                    this.Source = BitmapCache.KFNormalS;
                    break;
                case KeyImageTypes.KFEaseIn:
                    this.Source = BitmapCache.KFEaseIn;
                    break;
                case KeyImageTypes.KFEaseInS:
                    this.Source = BitmapCache.KFEaseInS;
                    break;
                case KeyImageTypes.KFEaseOut:
                    this.Source = BitmapCache.KFEaseOut;
                    break;
                case KeyImageTypes.KFEaseOutS:
                    this.Source = BitmapCache.KFEaseOutS;
                    break;
                case KeyImageTypes.KFEaseInOut:
                    this.Source = BitmapCache.KFEaseInOut;
                    break;
                case KeyImageTypes.KFEaseInOutS:
                    this.Source = BitmapCache.KFEaseInOutS;
                    break;
                case KeyImageTypes.KFCannotEase:
                    this.Source = BitmapCache.KFCannotEase;
                    break;
                case KeyImageTypes.KFCannotEaseS:
                    this.Source = BitmapCache.KFCannotEaseS;
                    break;
                case KeyImageTypes.KFHold:
                    this.Source = BitmapCache.KFHold;
                    break;
                case KeyImageTypes.KFHoldS:
                    this.Source = BitmapCache.KFHoldS;
                    break;
                case KeyImageTypes.KFEaseInHold:
                    this.Source = BitmapCache.KFEaseInHold;
                    break;
                case KeyImageTypes.KFEaseInHoldS:
                    this.Source = BitmapCache.KFEaseInHoldS;
                    break;
            }
        }

        private void Image_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (ContextMenu == null)
                SetupContextMenu();
            ContextMenu.IsOpen = true;
        }

        public void SetupContextMenu()
        {
            //Debug.WriteLine("Setting up context menu");
            // Create context menu
            MenuItem mi, mi2;
            Binding myBinding;
            ContextMenu cm = new ContextMenu();
            CustomKeyFrame kf = (CustomKeyFrame)DataContext;

            // EDIT VALUE
            mi = new MenuItem();
            mi.Header = "Edit Value";
            mi.Click += EditValueClicked;
            if (kf.KeyFrameType == CustomKeyFrameTypes.String)
                mi.IsEnabled = false;
            cm.Items.Add(mi);

            // EASING
            mi = new MenuItem();
            mi.Header = "Easing";

            // No ease
            mi2 = new MenuItem();
            mi2.Name = "NoEase";
            mi2.Header = "No Ease";
            //mi2.IsCheckable = true;
            myBinding = new Binding("HasNoEase");
            myBinding.Mode = BindingMode.OneWay;
            myBinding.Source = kf;
            mi2.SetBinding(MenuItem.IsCheckedProperty, myBinding);
            mi2.Command = KeyFrameOption_ClickedCommand;
            mi2.CommandParameter = KeyFramePropertyTypes.NoEase;
            mi.Items.Add(mi2);

            // Ease In
            mi2 = new MenuItem();
            mi2.Name = "EaseIn";
            mi2.Header = "Ease In";
            //mi2.IsCheckable = true;
            myBinding = new Binding("HasEaseIn");
            myBinding.Mode = BindingMode.OneWay;
            myBinding.Source = kf;
            mi2.SetBinding(MenuItem.IsCheckedProperty, myBinding);
            mi2.Command = KeyFrameOption_ClickedCommand;
            mi2.CommandParameter = KeyFramePropertyTypes.EaseIn;
            mi.Items.Add(mi2);

            // Ease Out
            mi2 = new MenuItem();
            mi2.Name = "EaseOut";
            mi2.Header = "Ease Out";
            //mi2.IsCheckable = true;
            myBinding = new Binding("HasEaseOut");
            myBinding.Mode = BindingMode.OneWay;
            myBinding.Source = kf;
            mi2.SetBinding(MenuItem.IsCheckedProperty, myBinding);
            myBinding = new Binding("HasHoldValue");
            myBinding.Mode = BindingMode.OneWay;
            myBinding.Converter = new BoolInvertConverter();
            myBinding.Source = kf;
            mi2.SetBinding(MenuItem.IsEnabledProperty, myBinding);
            mi2.Command = KeyFrameOption_ClickedCommand;
            mi2.CommandParameter = KeyFramePropertyTypes.EaseOut;
            mi.Items.Add(mi2);

            // Ease InOut
            mi2 = new MenuItem();
            mi2.Name = "EaseInOut";
            mi2.Header = "Ease In&Out";
            //mi2.IsCheckable = true;
            myBinding = new Binding("HasEaseInOut");
            myBinding.Mode = BindingMode.OneWay;
            myBinding.Source = kf;
            mi2.SetBinding(MenuItem.IsCheckedProperty, myBinding);
            myBinding = new Binding("HasHoldValue");
            myBinding.Mode = BindingMode.OneWay;
            myBinding.Converter = new BoolInvertConverter();
            myBinding.Source = kf;
            mi2.SetBinding(MenuItem.IsEnabledProperty, myBinding);
            mi2.Command = KeyFrameOption_ClickedCommand;
            mi2.CommandParameter = KeyFramePropertyTypes.EaseInOut;
            mi.Items.Add(mi2);

            cm.Items.Add(mi);

            if (kf.KeyFrameType == CustomKeyFrameTypes.Double)
            {
                // TIME INTERPOLATION METHOD
                mi = new MenuItem();
                mi.Header = "Interpolation";

                mi2 = new MenuItem();
                mi2.Header = "Linear";
                mi2.Name = "Linear";
                //mi2.IsCheckable = true;
                myBinding = new Binding("HasLinearInterpolation");
                myBinding.Mode = BindingMode.OneWay;
                myBinding.Source = kf;
                mi2.SetBinding(MenuItem.IsCheckedProperty, myBinding);
                mi2.Command = KeyFrameOption_ClickedCommand;
                mi2.CommandParameter = KeyFramePropertyTypes.InterpolationLinear;
                mi.Items.Add(mi2);

                mi2 = new MenuItem();
                mi2.Header = "Add Elapsed Time";
                mi2.Name = "AddElapsed";
                //mi2.IsCheckable = true;
                myBinding = new Binding("HasAddElapsedInterpolation");
                myBinding.Mode = BindingMode.OneWay;
                myBinding.Source = kf;
                mi2.SetBinding(MenuItem.IsCheckedProperty, myBinding);
                mi2.Command = KeyFrameOption_ClickedCommand;
                mi2.CommandParameter = KeyFramePropertyTypes.InterpolationAddElapsed;
                mi.Items.Add(mi2);

                cm.Items.Add(mi);
            } 
            else if (kf.KeyFrameType == CustomKeyFrameTypes.Point3D)
            {
                // 3D INTERPOLATION
                mi = new MenuItem();
                mi.Header = "Interpolation";

                mi2 = new MenuItem();
                mi2.Header = "Linear:Linear";
                mi2.Name = "LinearLinear";
                //mi2.IsCheckable = true;
                myBinding = new Binding("HasLinearLinear");
                myBinding.Mode = BindingMode.OneWay;
                myBinding.Source = kf;
                mi2.SetBinding(MenuItem.IsCheckedProperty, myBinding);
                mi2.Command = KeyFrameOption_ClickedCommand;
                mi2.CommandParameter = KeyFramePropertyTypes.InterpolationLinearLinear;
                mi.Items.Add(mi2);

                mi2 = new MenuItem();
                mi2.Header = "Linear:Bezier";
                mi2.Name = "LinearBezier";
                //mi2.IsCheckable = true;
                myBinding = new Binding("HasLinearBezier");
                myBinding.Mode = BindingMode.OneWay;
                myBinding.Source = kf;
                mi2.SetBinding(MenuItem.IsCheckedProperty, myBinding);
                mi2.Command = KeyFrameOption_ClickedCommand;
                mi2.CommandParameter = KeyFramePropertyTypes.InterpolationLinearBezier;
                mi.Items.Add(mi2);

                mi2 = new MenuItem();
                mi2.Header = "Bezier:Linear";
                mi2.Name = "BezierLinear";
                //mi2.IsCheckable = true;
                myBinding = new Binding("HasBezierLinear");
                myBinding.Mode = BindingMode.OneWay;
                myBinding.Source = kf;
                mi2.SetBinding(MenuItem.IsCheckedProperty, myBinding);
                mi2.Command = KeyFrameOption_ClickedCommand;
                mi2.CommandParameter = KeyFramePropertyTypes.InterpolationBezierLinear;
                mi.Items.Add(mi2);

                mi2 = new MenuItem();
                mi2.Header = "Bezier:Bezier";
                mi2.Name = "BezierBezier";
                //mi2.IsCheckable = true;
                myBinding = new Binding("HasBezierBezier");
                myBinding.Mode = BindingMode.OneWay;
                myBinding.Source = kf;
                mi2.SetBinding(MenuItem.IsCheckedProperty, myBinding);
                mi2.Command = KeyFrameOption_ClickedCommand;
                mi2.CommandParameter = KeyFramePropertyTypes.InterpolationBezierBezier;
                mi.Items.Add(mi2);

                mi.Items.Add(new Separator());

                mi2 = new MenuItem();
                mi2.Header = "Auto Bezier";
                mi2.Name = "AutoBezier";
                //mi2.IsCheckable = true;
                myBinding = new Binding("HasAutoBezier");
                myBinding.Mode = BindingMode.OneWay;
                myBinding.Source = kf;
                mi2.SetBinding(MenuItem.IsCheckedProperty, myBinding);
                mi2.Command = KeyFrameOption_ClickedCommand;
                mi2.CommandParameter = KeyFramePropertyTypes.AutoBezier;
                mi.Items.Add(mi2);

                cm.Items.Add(mi);
            }

            cm.Items.Add(new Separator());

            mi = new MenuItem();
            mi.Header = "Toggle Hold";
            mi.Name = "ToggleHold";
            myBinding = new Binding("HasHoldValue");
            myBinding.Mode = BindingMode.OneWay;
            myBinding.Source = kf;
            mi.SetBinding(MenuItem.IsCheckedProperty, myBinding);
            myBinding = new Binding("HasAddElapsedInterpolation");
            myBinding.Mode = BindingMode.OneWay;
            myBinding.Converter = new BoolInvertConverter();
            myBinding.Source = kf;
            mi.SetBinding(MenuItem.IsEnabledProperty, myBinding);
            mi.Command = KeyFrameOption_ClickedCommand;
            mi.CommandParameter = KeyFramePropertyTypes.Hold;

            cm.Items.Add(mi);

            this.ContextMenu = cm;
        }

        private void EditValueClicked(object sender, RoutedEventArgs e)
        {
            e.Handled = true;

            double doubleOrig;
            EditPoint3DDataTemplate pointOrig;
            CustomKeyFrame kf = (CustomKeyFrame)DataContext;
            CustomDoubleKeyFrame kfd;
            CustomPoint3DKeyFrame kfp;

            if (kf.KeyFrameType == CustomKeyFrameTypes.Double)
            {
                kfd = (CustomDoubleKeyFrame)kf;
                doubleOrig = kfd.Value;
                EditDoubleValueDialogBox dlg = new EditDoubleValueDialogBox();
                dlg.DataContext = kfd;
                dlg.Owner = Application.Current.MainWindow;
                dlg.ShowDialog();

                if (dlg.DialogResult == true)
                {
                    ServicesFactory.EventService.GetEvent<CompLayerPropertyChangedEvent>().Publish(new CompLayerPropertyChangedEventArgs() { Sender = this, PropertyType = "Value", UpdateCurrentValues = true });
                    if (doubleOrig != kfd.Value)
                    {
                        LfsRecordController.RecordUserAction(new UserActionKeyFrameDoubleValueChanged()
                        {
                            LayerIndex = kf.ParentLayer.LayerIndex,
                            PropertyIndex = kf.ParentProperty.PropertyIndex,
                            KeyFrameIndex = kf.KeyFrameIndex,
                            ValueOffset = kfd.Value - doubleOrig
                        });
                        LfsRecordController.CreateUndoLevel();
                    }
                }
                else
                {
                    // Cancelled
                    kfd.Value = doubleOrig;
                }
            }
            else if (kf.KeyFrameType == CustomKeyFrameTypes.String)
            {

            }
            else
            {
                kfp = (CustomPoint3DKeyFrame)kf;
                pointOrig = new EditPoint3DDataTemplate();
                pointOrig.xValue = kfp.xValue;
                pointOrig.yValue = kfp.yValue;
                pointOrig.zValue = kfp.zValue;
                EditPoint3DValueDialogBox dlg = new EditPoint3DValueDialogBox();
                dlg.DataContext = pointOrig;
                dlg.Owner = Application.Current.MainWindow;
                dlg.ShowDialog();

                if (dlg.DialogResult == true)
                {
                    if (kfp.Value.X != pointOrig.xValue ||
                        kfp.Value.Y != pointOrig.yValue ||
                        kfp.Value.Z != pointOrig.zValue)
                    {
                        LfsRecordController.RecordUserAction(new UserActionKeyFramePoint3DValueChanged()
                        {
                            LayerIndex = kf.ParentLayer.LayerIndex,
                            PropertyIndex = kf.ParentProperty.PropertyIndex,
                            KeyFrameIndex = kf.KeyFrameIndex,
                            ValueOffset = new Vector3D(pointOrig.xValue - kfp.Value.X, pointOrig.yValue - kfp.Value.Y, pointOrig.zValue - kfp.Value.Z)
                        });

                        kfp.Value = new Point3D(
                            pointOrig.xValue,
                            pointOrig.yValue,
                            pointOrig.zValue
                            );

                        LfsRecordController.CreateUndoLevel();
                    }
                    kf.OnPropertyChanged("ReCacheDummyWithAutoBezierCore");
                    ServicesFactory.EventService.GetEvent<CompLayerPropertyChangedEvent>().Publish(new CompLayerPropertyChangedEventArgs() { Sender = this, PropertyType = "Value", UpdateCurrentValues = true });
                }
            }
        }

        // KeyFrameOption_ClickedCommand
        RelayCommand _keyFrameOption_ClickedCommand;
        public ICommand KeyFrameOption_ClickedCommand
        {
            get
            {
                if (_keyFrameOption_ClickedCommand == null)
                    _keyFrameOption_ClickedCommand = new RelayCommand(param => this.KeyFrameProperty_ClickedCommandExecute(param),
                                                                      param => this.KeyFrameProperty_ClickedCommandCanExecute(param));
                return _keyFrameOption_ClickedCommand;
            }
        }
        bool KeyFrameProperty_ClickedCommandCanExecute(object param)
        {
            if (param == null)
                return true;

            CustomKeyFrame kf = (CustomKeyFrame)DataContext;
            KeyFramePropertyTypes kt = (KeyFramePropertyTypes)param;
            if (kt == KeyFramePropertyTypes.AutoBezier)
                return (kf.HasLinearLinear) ? false : true;
            return true;
        }
        void KeyFrameProperty_ClickedCommandExecute(object optionType)
        {
            CustomKeyFrame kf = (CustomKeyFrame)DataContext;
            KeyFramePropertyTypes ot = (KeyFramePropertyTypes)optionType;
            //Debug.WriteLine("Option checked : " + ot);

            CustomKeyFrameOptions oldOptions = kf.Options;
            KeyFrameInterpolationTypes oldIntIn = kf.InterpolationTypeIn;
            KeyFrameInterpolationTypes oldIntOut = kf.InterpolationTypeOut;
            double oldEaseIn = kf.EaseInValue;
            double oldEaseOut = kf.EaseOutValue;

            switch (ot)
            {
                case KeyFramePropertyTypes.NoEase:
                    kf.EaseInValue = CustomDoubleEase.NoEase;
                    kf.EaseOutValue = CustomDoubleEase.NoEase;
                    break;
                case KeyFramePropertyTypes.EaseIn:
                    kf.EaseInValue = CustomDoubleEase.DefaultInValue;
                    kf.EaseOutValue = CustomDoubleEase.NoEase;
                    break;
                case KeyFramePropertyTypes.EaseOut:
                    kf.EaseInValue = CustomDoubleEase.NoEase;
                    kf.EaseOutValue = CustomDoubleEase.DefaultOutValue;
                    break;
                case KeyFramePropertyTypes.EaseInOut:
                    kf.EaseInValue = CustomDoubleEase.DefaultInValue;
                    kf.EaseOutValue = CustomDoubleEase.DefaultOutValue;
                    break;
                case KeyFramePropertyTypes.InterpolationLinearLinear:
                    kf.InterpolationTypeIn = kf.InterpolationTypeOut = KeyFrameInterpolationTypes.Linear;
                    break;
                case KeyFramePropertyTypes.InterpolationLinearBezier:
                    kf.InterpolationTypeIn = KeyFrameInterpolationTypes.Linear;
                    kf.InterpolationTypeOut = KeyFrameInterpolationTypes.Bezier;
                    break;
                case KeyFramePropertyTypes.InterpolationBezierLinear:
                    kf.InterpolationTypeIn = KeyFrameInterpolationTypes.Bezier;
                    kf.InterpolationTypeOut = KeyFrameInterpolationTypes.Linear;
                    break;
                case KeyFramePropertyTypes.InterpolationBezierBezier:
                    kf.InterpolationTypeIn = kf.InterpolationTypeOut = KeyFrameInterpolationTypes.Bezier;
                    break;
                case KeyFramePropertyTypes.AutoBezier:
                    kf.Options ^= CustomKeyFrameOptions.AutoBezier;
                    break;
                case KeyFramePropertyTypes.Hold:
                    kf.Options ^= CustomKeyFrameOptions.Hold;
                    break;
                case KeyFramePropertyTypes.InterpolationLinear:
                    kf.InterpolationTypeOut = KeyFrameInterpolationTypes.Linear;
                    break;
                case KeyFramePropertyTypes.InterpolationAddElapsed:
                    kf.InterpolationTypeOut = KeyFrameInterpolationTypes.AddElapsed;
                    kf.Options &= ~CustomKeyFrameOptions.Hold;
                    break;
            }

            if (kf.Options != oldOptions)
            {
                LfsRecordController.RecordUserAction(new UserActionKeyFrameOptionChanged()
                {
                    LayerIndex = kf.ParentLayer.LayerIndex,
                    PropertyIndex = kf.ParentProperty.PropertyIndex,
                    KeyFrameIndex = kf.KeyFrameIndex,
                    OldOptions = oldOptions,
                    NewOptions = kf.Options
                });
            }
            if (kf.EaseInValue != oldEaseIn || kf.EaseOutValue != oldEaseOut)
            {
                LfsRecordController.RecordUserAction(new UserActionKeyFrameEaseChanged()
                {
                    LayerIndex = kf.ParentLayer.LayerIndex,
                    PropertyIndex = kf.ParentProperty.PropertyIndex,
                    KeyFrameIndex = kf.KeyFrameIndex,
                    OldEaseIn = oldEaseIn,
                    NewEaseIn = kf.EaseInValue,
                    OldEaseOut = oldEaseOut,
                    NewEaseOut = kf.EaseOutValue
                });
            }
            if (kf.InterpolationTypeIn != oldIntIn || kf.InterpolationTypeOut != oldIntOut)
            {
                LfsRecordController.RecordUserAction(new UserActionKeyFrameInterpolationChanged()
                {
                    LayerIndex = kf.ParentLayer.LayerIndex,
                    PropertyIndex = kf.ParentProperty.PropertyIndex,
                    KeyFrameIndex = kf.KeyFrameIndex,
                    OldIntIn = oldIntIn,
                    NewIntIn = kf.InterpolationTypeIn,
                    OldIntOut = oldIntOut,
                    NewIntOut = kf.InterpolationTypeOut
                });
            }
            LfsRecordController.CreateUndoLevel();

            ServicesFactory.EventService.GetEvent<CompLayerPropertyChangedEvent>().Publish(new CompLayerPropertyChangedEventArgs() { Sender = this, PropertyType = "KeyFrameOptions", UpdateCurrentValues = true });
        }
    }
}
